package com.gengzp.common.utils;

import com.gengzp.common.exception.BusinessException;
import jakarta.annotation.PreDestroy;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName DistributedLockUtils
 * @Description 分布式锁工具类
 * @Author gengzp
 * @Date 2025/9/15 15:42
 */
@Component
public class DistributedLockUtils {

    private static final Logger logger = LoggerFactory.getLogger(DistributedLockUtils.class);

    // ============================ 静态常量（不变）============================
    private static final String LOCK_KEY_PREFIX = "distributed_lock:";
    private static final String UNLOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) " +
            "else return 0 end";
    private static final long DEFAULT_LOCK_TIMEOUT = 30;    // 默认锁超时：30秒
    private static final long DEFAULT_WAIT_TIMEOUT = 10;    // 默认等待超时：10秒
    private static final long DEFAULT_RETRY_INTERVAL = 500; // 默认重试间隔：500毫秒

    // ============================ 静态资源（需初始化/注入）============================
    // 1. Redis模板（Spring注入后赋值给静态变量）
    private static RedisTemplate<String, Object> redisTemplate;
    // 2. 看门狗线程池（静态初始化，全局唯一）
    private static final ScheduledExecutorService watchdogExecutor;
    // 3. 线程本地存储（存储当前线程持有的锁信息，支持可重入）
    private static final ThreadLocal<Map<String, LockInfo>> threadLocalLockInfo;
    // 4. 预编译释放锁脚本（静态初始化，提升性能）
    private static final RedisScript<Long> unlockScript;

    // ============================ 静态初始化（类加载时执行）============================
    static {
        // 初始化看门狗线程池（守护线程，避免阻塞JVM退出）
        watchdogExecutor = Executors.newScheduledThreadPool(
                Runtime.getRuntime().availableProcessors(),
                new ThreadFactory() {
                    private final AtomicInteger threadNumber = new AtomicInteger(1);

                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r, "distributed-lock-watchdog-" + threadNumber.getAndIncrement());
                        thread.setDaemon(true);
                        return thread;
                    }
                }
        );

        // 初始化线程本地存储（每个线程独立副本）
        threadLocalLockInfo = ThreadLocal.withInitial(HashMap::new);

        // 预编译释放锁脚本
        unlockScript = RedisScript.of(UNLOCK_LUA_SCRIPT, Long.class);
    }

    // ============================ RedisTemplate 静态注入（关键）============================
    @Autowired
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        DistributedLockUtils.redisTemplate = redisTemplate;
    }

    // ============================ 锁信息静态内部类============================
    private static class LockInfo {
        String lockId;          // 锁唯一标识
        int reentrantCount;     // 重入次数
        ScheduledFuture<?> watchdogFuture; // 看门狗任务句柄
    }

    // ============================ 全静态业务方法（外部直接调用）============================

    /**
     * 【静态方法】组装分布式锁完整key
     */
    public static String buildDistributedLockKey(String lockKey) {
        if (StringUtils.isBlank(lockKey)) {
            throw BusinessException.get("自定义的分布式锁key不能为空");
        }
        return LOCK_KEY_PREFIX + lockKey;
    }

    /**
     * 【静态方法】校验分布式锁是否存在
     */
    public static boolean verifyDistributedLockIsExist(String lockKey) {
        if (redisTemplate == null) {
            throw new IllegalStateException("RedisTemplate未初始化，请确保DistributedLockUtils已被Spring扫描为Bean");
        }
        try {
            Object value = redisTemplate.opsForValue().get(buildDistributedLockKey(lockKey));
            return value != null;
        } catch (Exception e) {
            logger.error("校验分布式锁存在性失败，lockKey:{}", lockKey, e);
            throw BusinessException.get("校验分布式锁存在性失败: " + e.getMessage());
        }
    }

    /**
     * 【静态方法】执行带返回值的分布式锁保护方法（自定义参数）
     */
    public static <T> T doExecuteWithLock(DistributedLockTemplate<T> template, String lockKey,
                                          long lockTimeout, long waitTimeout, long retryInterval) {
        if (redisTemplate == null) {
            throw new IllegalStateException("RedisTemplate未初始化，请确保DistributedLockUtils已被Spring扫描为Bean");
        }
        String fullLockKey = buildDistributedLockKey(lockKey);
        boolean locked = false;

        try {
            locked = tryLock(fullLockKey, lockTimeout, waitTimeout, retryInterval);
            if (!locked) {
                throw BusinessException.get("获取锁失败，等待超时（lockKey:" + lockKey + "）");
            }
            logger.info("获取分布式锁成功，锁key:{}", fullLockKey);
            return template.doExecuteWithLock();
        } finally {
            if (locked) {
                releaseLock(fullLockKey);
            }
        }
    }

    /**
     * 【静态方法】执行带返回值的分布式锁保护方法（默认参数）
     */
    public static <T> T doExecuteWithLock(DistributedLockTemplate<T> template, String lockKey) {
        return doExecuteWithLock(template, lockKey,
                DEFAULT_LOCK_TIMEOUT, DEFAULT_WAIT_TIMEOUT, DEFAULT_RETRY_INTERVAL);
    }

    /**
     * 【静态方法】执行无返回值的分布式锁保护方法（自定义参数）
     */
    public static void doExecuteWithLock(Runnable task, String lockKey,
                                         long lockTimeout, long waitTimeout, long retryInterval) {
        doExecuteWithLock(() -> {
            task.run();
            return null;
        }, lockKey, lockTimeout, waitTimeout, retryInterval);
    }

    /**
     * 【静态方法】执行无返回值的分布式锁保护方法（默认参数）
     */
    public static void doExecuteWithLock(Runnable task, String lockKey) {
        doExecuteWithLock(task, lockKey,
                DEFAULT_LOCK_TIMEOUT, DEFAULT_WAIT_TIMEOUT, DEFAULT_RETRY_INTERVAL);
    }

    // ============================ 静态辅助方法（内部调用）============================

    /**
     * 【静态辅助】尝试获取锁（支持可重入、重试）
     */
    private static boolean tryLock(String fullLockKey, long lockTimeout, long waitTimeout, long retryInterval) {
        // 1. 检查是否已持有该锁（支持可重入）
        Map<String, LockInfo> lockMap = threadLocalLockInfo.get();
        if (lockMap.containsKey(fullLockKey)) {
            LockInfo info = lockMap.get(fullLockKey);
            info.reentrantCount++;
            logger.debug("重入获取锁成功，锁key:{}, 重入次数:{}", fullLockKey, info.reentrantCount);
            return true;
        }

        // 2. 计算等待截止时间
        long deadline = System.currentTimeMillis() + waitTimeout * 1000;
        String lockId = generateLockId();

        try {
            // 3. 循环重试获取锁
            while (System.currentTimeMillis() <= deadline) {
                Boolean success = redisTemplate.opsForValue()
                        .setIfAbsent(fullLockKey, lockId, lockTimeout, TimeUnit.SECONDS);

                if (Boolean.TRUE.equals(success)) {
                    // 4. 获取锁成功：记录锁信息+启动看门狗
                    LockInfo info = new LockInfo();
                    info.lockId = lockId;
                    info.reentrantCount = 1;
                    info.watchdogFuture = startWatchdog(fullLockKey, lockId, lockTimeout);
                    lockMap.put(fullLockKey, info);
                    logger.debug("首次获取锁成功，锁key:{}, 锁标识:{}", fullLockKey, lockId);
                    return true;
                }

                // 5. 获取失败：等待重试
                logger.debug("获取锁失败，{}ms后重试，锁key:{}", retryInterval, fullLockKey);
                Thread.sleep(retryInterval);
            }

            // 6. 等待超时
            logger.warn("获取锁等待超时，锁key:{}, 等待时间:{}s", fullLockKey, waitTimeout);
            return false;
        } catch (InterruptedException e) {
            logger.error("获取锁被中断，锁key:{}", fullLockKey, e);
            Thread.currentThread().interrupt(); // 恢复中断状态
            return false;
        } catch (Exception e) {
            logger.error("获取锁异常，锁key:{}", fullLockKey, e);
            throw BusinessException.get("获取分布式锁异常: " + e.getMessage());
        }
    }

    /**
     * 【静态辅助】释放锁
     */
    private static void releaseLock(String fullLockKey) {
        Map<String, LockInfo> lockMap = threadLocalLockInfo.get();
        LockInfo info = lockMap.get(fullLockKey);

        if (info == null) {
            logger.warn("释放锁失败：当前线程未持有该锁，锁key:{}", fullLockKey);
            return;
        }

        // 1. 重入次数减1（未到0则不实际释放）
        info.reentrantCount--;
        if (info.reentrantCount > 0) {
            logger.debug("释放锁（重入计数减少），锁key:{}, 剩余重入次数:{}", fullLockKey, info.reentrantCount);
            return;
        }

        // 2. 重入次数为0：实际释放锁
        try {
            Long result = redisTemplate.execute(unlockScript, Collections.singletonList(fullLockKey), info.lockId);
            if (result != null && result > 0) {
                logger.info("释放锁成功，锁key:{}, 锁标识:{}", fullLockKey, info.lockId);
            } else {
                logger.warn("释放锁失败：锁已过期或被其他线程持有，锁key:{}, 锁标识:{}", fullLockKey, info.lockId);
            }
        } catch (Exception e) {
            logger.error("释放锁异常，锁key:{}, 锁标识:{}", fullLockKey, info.lockId, e);
            throw BusinessException.get("释放分布式锁异常: " + e.getMessage());
        } finally {
            // 3. 取消看门狗任务+清理ThreadLocal
            if (info.watchdogFuture != null) {
                info.watchdogFuture.cancel(true);
            }
            lockMap.remove(fullLockKey);
            if (lockMap.isEmpty()) {
                threadLocalLockInfo.remove(); // 避免ThreadLocal内存泄漏
            }
        }
    }

    /**
     * 【静态辅助】启动看门狗自动续期
     */
    private static ScheduledFuture<?> startWatchdog(String fullLockKey, String lockId, long lockTimeout) {
        long refreshInterval = (lockTimeout * 1000) / 3; // 续期间隔=锁超时的1/3

        return watchdogExecutor.scheduleAtFixedRate(() -> {
            try {
                // 校验锁是否仍属于当前线程
                Object currentLockId = redisTemplate.opsForValue().get(fullLockKey);
                if (currentLockId != null && lockId.equals(currentLockId.toString())) {
                    redisTemplate.expire(fullLockKey, lockTimeout, TimeUnit.SECONDS);
                    logger.debug("看门狗续期成功，锁key:{}, 锁标识:{}, 续期时间:{}s",
                            fullLockKey, lockId, lockTimeout);
                } else {
                    logger.debug("锁已失效，停止续期，锁key:{}, 锁标识:{}", fullLockKey, lockId);
                    throw new ConcurrentModificationException("锁已被释放或修改");
                }
            } catch (Exception e) {
                logger.error("看门狗续期异常，锁key:{}, 锁标识:{}", fullLockKey, lockId, e);
                throw new RuntimeException("Watchdog refresh failed", e);
            }
        }, refreshInterval, refreshInterval, TimeUnit.MILLISECONDS);
    }

    /**
     * 【静态辅助】生成轻量锁标识（线程ID-时间戳-短UUID）
     */
    private static String generateLockId() {
        return Thread.currentThread().getId() + "-" + System.currentTimeMillis() + "-" +
                UUID.randomUUID().toString().substring(0, 8);
    }

    // ============================ 资源销毁（Spring管理）============================

    /**
     * Spring销毁Bean时调用，关闭看门狗线程池（避免资源泄漏）
     */
    @PreDestroy
    public void destroy() {
        logger.info("开始销毁分布式锁工具类：关闭看门狗线程池");
        if (!watchdogExecutor.isShutdown()) {
            watchdogExecutor.shutdown();
            try {
                // 等待1秒让任务结束，超时则强制关闭
                if (!watchdogExecutor.awaitTermination(1, TimeUnit.SECONDS)) {
                    watchdogExecutor.shutdownNow();
                }
            } catch (InterruptedException e) {
                watchdogExecutor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
        logger.info("分布式锁工具类销毁完成");
    }

    // ============================ 函数式接口（不变）============================
    @FunctionalInterface
    public interface DistributedLockTemplate<T> {
        T doExecuteWithLock();
    }
}